#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "bib_Socket.h"

int TIEMPO;

int conectarKss(int *socketCli);
char solicitarUsrH();
void ingresarCadena(char *cadena, int max);
void funcionFileSystem(int sock);
void funcionPlanificador(int sock);
uint32_t solicitarSectorUsr();
void solicitarRetardoPlanifUsr();

int main(void){
	int socketCli=0;
	char entradaUsr=0;
	uint8_t idPlanif;
	int ejecutarPlanif =0;

	struct HEADER_NIPC header;
	char mensaje[100];

	//Conectarse al PRAID
	if (conectarKss(&socketCli)){
		printf("Error al conectarse\n");
		return EXIT_FAILURE;
	}

	//Solicitar al usuario una operacion
	entradaUsr=solicitarUsrH();

	if (entradaUsr=='1'){
		//Planificador

		ejecutarPlanif=1;

		//Ingresar identificador de planificador
		printf("Ingrese un identificador numérico: ");
		scanf("%u",&idPlanif);

		header.payloadLength=sizeof(uint8_t);
		header.type=0;

		enviarMensajeNIPC(socketCli, &header, (char *) &idPlanif);

	}else{
		//File System
		srand (time(NULL));
		header.payloadLength=0;
		header.type=0;

		enviarMensajeNIPC(socketCli, &header, NULL);
	}

	//Recibir respuesta
	mensaje[0]='\0';
	if (recibirMensajeNIPC(socketCli, &header, mensaje)){
		printf("El simulador finalizó por error en la conexión\n");
		return EXIT_FAILURE;
	}

	if (header.type == 0){
		if (header.payloadLength>0){
			mensaje[header.payloadLength]='\0';
			printf("El PRAID rechazo la conexión, la razón:\n%s\n",mensaje);
			return(0);
		}else{
			printf("El PRAID acepto la conexión\n");
		}

	}

	//Delegar según función
	if (ejecutarPlanif){
		//Planificador
		funcionPlanificador(socketCli);

	}else{
		//File System
		funcionFileSystem(socketCli);
	}

	cerrarSocket(socketCli);
	printf("El simulador finalizo\n");
	return (0);
}

int conectarKss(int *socketCli){
	char IP[] = "127.0.0.1";

	//Establecer conexion
	if (abrirSocket(socketCli)){
		return EXIT_FAILURE;
	}

	//Conectar al KSS
	if (conectar(*socketCli, PUERTO, IP)){
		cerrarSocket(*socketCli);
		printf("No se pudo conectar con el PRAID\n");
		return EXIT_FAILURE;
	}

	//Notificar que se conecto al PRAID
	printf("Se establecio la conexion con el PRAID\n");

	//Exito con la conexión
	return EXIT_SUCCESS;
}

char solicitarUsrH(){
	char entrada=0;

	while(entrada != '1' && entrada != '2'){
		printf("Ingrese un comando:\n1-Conectarse como planificador\n2-Conectarse como File System\n");

		entrada=getchar();
	}

	return entrada;
}

//Solicitudes del file system
void solicitarUsrOpcFS(char *comandos){
	char entrada=0;

	//Solicitar operación
	while(entrada != '1' && entrada != '2'){
		printf("Ingrese un comando:\n1-Enviar una solicitud y esperar respuesta\n2-Terminar\n");

		entrada=getchar();
	}
	comandos[0]=entrada;

	//Solicitar lectura o escritura
	entrada=0;
	while(entrada != '1' && entrada != '2'){
		printf("Ingrese un comando:\n1-Lectura\n2-Escritura\n");

		entrada=getchar();
	}
	comandos[1]=entrada;

	return;
}

void ingresarCadena(char *cadena, int max){
	int i;

	printf("Ingrese una cadena, max %d caracteres (*: para finalizar):\n",max);

	for (i=0; i < max; i++){
		cadena[i]=getchar();
		if (cadena[i]=='*'){
			cadena[i]='\0';
			return;
		}
	}

	cadena[i+1]='\0';
}

void funcionFileSystem(int sock){
	int i;

	char opcion[2];
	HEADER_NIPC header;
	////char datos[513];	//Supuest. no se usa
	struct PAYLOAD_LECTURA solicLect;
	struct PAYLOAD_ESCRITURA solicEsc;
	struct PAYLOAD_RESPUESTA_LECTURA respLect;
	char *respRecibida;

	//Solicitar opcion al usuario
	solicitarUsrOpcFS(opcion);

	//Lecturas o escrituras
	if (opcion[1]=='1'){
		//Lectura
		header.type=1;
	}else{
		//Escritura
		header.type=2;
	}


	if (opcion[0]=='1'){
		//Enviar una solicitud

		//Si es escritura, el mensaje
		if (header.type==2){
			solicEsc.sector=solicitarSectorUsr();
			solicEsc.id=rand() % 100;

			strcpy(solicEsc.buffer,"Hola Mundo! (Unico mensaje)");
			for (i=strlen(solicEsc.buffer);i<512;i++){
				solicEsc.buffer[i]='.';
			}
			header.payloadLength=sizeof(struct PAYLOAD_ESCRITURA);

			if (enviarMensajeNIPC(sock, &header, (char *) &solicEsc))
				printf("***Error al enviar solicitud\n");

		}else{
			solicLect.sector=solicitarSectorUsr();
			solicLect.id=rand() % 100;
			header.payloadLength=sizeof(struct PAYLOAD_LECTURA);

			if (enviarMensajeNIPC(sock, &header, (char *) &solicLect))
				printf("***Error al enviar solicitud\n");

		}

		//Esperar mensaje
		printf("Esperando respuesta...\n");
		if (recibirMensajeNIPCDinamico(sock, &header, &respRecibida)){
			printf("El simulador finalizó por error en la conexión\n");
			return;
		}

		//Mostrar respuesta
		if (header.type==10){
			printf("El PRAID respondio error\n");
		}else{
			if (header.type==2){
				//Resp. de Escritura
				printf("La escritura se realizo correctamente\n");

			}else{
				//Resp. de Lectura
				printf("La lectura se realizo correctamente\n");

				memcpy(&respLect, respRecibida, sizeof(PAYLOAD_RESPUESTA_LECTURA));

				free(respRecibida);

				printf("Cadena:\n%s\n",respLect.buffer);


			}

		}

	}else{
		//Terminar.
		printf("Función no soportada:\n");
		return;
	}

}

void funcionPlanificador(int sock){
	struct HEADER_NIPC header;
	char *mensaje;
	struct EST_CHS chs;

	struct PAYLOAD_LECTURA mensajeRecibidoLect;
	struct PAYLOAD_ESCRITURA mensajeRecibidoEsc;

	struct PAYLOAD_RESPUESTA_LECTURA respLect;

	uint32_t idSolicitud;

	solicitarRetardoPlanifUsr();

	printf("***Esperando solicitud\n");

	while (1){
		//Recibir solicitud

		if (recibirMensajeNIPCDinamico(sock, &header, &mensaje)){
			//Error
			printf("***Error al recibir\n");
			return;
		}

		switch(header.type){
		case 1:

			if (TIEMPO >0)
				sleep(TIEMPO);

			//Obtener el id
			memcpy(&mensajeRecibidoLect, mensaje, sizeof(struct PAYLOAD_LECTURA));
			free(mensaje);

			if (mensajeRecibidoLect.sector % 50000 == 0)
				printf("Se solicito una lectura en el sector: %d\n",mensajeRecibidoLect.sector);

			//Generar mensaje y enviar
			respLect.id=mensajeRecibidoLect.id;
			strcpy(respLect.buffer, "Una cadena muuuuuuuuuuuyy larga, deberian ser 512, pero no puedo escribir tanto..jeejje =)\nChiky Gato!");

			header.type=TIPO_LECTURA;
			header.payloadLength=sizeof(struct PAYLOAD_RESPUESTA_LECTURA);

			if (enviarMensajeNIPC(sock, &header, (char *)&respLect)){
				printf("***Error al enviar la lectura OK\n");
				return;
			}
			break;

		case 2:

			if (TIEMPO >0)
				sleep(TIEMPO);

			//Obtener el id
			memcpy(&mensajeRecibidoEsc, mensaje, sizeof(struct PAYLOAD_ESCRITURA));
			free(mensaje);

			if (mensajeRecibidoEsc.sector % 50000 == 0)
				printf("Se solicito una escritura en el sector: %d\n",mensajeRecibidoEsc.sector);

			//Generar mensaje y enviar
			idSolicitud=mensajeRecibidoEsc.id;

			header.type=TIPO_ESCRITURA;
			header.payloadLength=sizeof(uint32_t);

			if (enviarMensajeNIPC(sock, &header, (char *)&idSolicitud)){
				printf("***Error al enviar el la escritura OK\n");
				return;
			}
			break;

		case 4:
			//Devolver cantidad de sectores

			printf("Se solicito la cantidad de sectores\n");

			if (TIEMPO >0)
				sleep(TIEMPO);

			//Enviar CHS
			chs.sector=4195;
			chs.cilindro=1000;	////Son alrededor de 2 GB

			//chs.sector=10485;
			//chs.cilindro=100;	////Son alrededor de 512 MB

			//chs.sector=5485;
			//chs.cilindro=100;	////Son alrededor de 256 MB

			chs.sector=1024;
			chs.cilindro=1024;

			chs.cabezal=1;
			header.type=TIPO_SECTORES;
			header.payloadLength=sizeof(struct EST_CHS);
			if (enviarMensajeNIPC(sock, &header, (char *)&chs)){
				printf("***Error al enviar el CHS\n");
				return;
			}
			break;

		case 7:
			printf("***Se recibio el mensaje de fin del proceso de espejado\n");
			break;

		default:
			////

			printf("Type invalido: %u\n", header.type);

			if (header.payloadLength>0)
				free(mensaje);

			break;
		}

	}
}

//Pide al usuario que ingrese un número de sector
uint32_t solicitarSectorUsr(){
	uint32_t entrada;

	printf("Ingrese el sector a leer/escribir:");

	scanf("%u",&entrada);

	return entrada;
}

//Solicitar retardo al usuario
void solicitarRetardoPlanifUsr(){
	int entrada;

	printf("Ingrese el retardo del planificador (Seg):");

	scanf("%d",&entrada);

	TIEMPO=entrada;
}
